package com.xiaobai.easyexcel.dynamiccolumn;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.istack.internal.NotNull;
import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
import org.apache.poi.util.IOUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

/**
 * @author wangtw
 * @ClassName DynamicColumnExport
 * @description: EasyExcel动态列导出
 * @date 2024/2/918:07
 */
public class DynamicColumnExport {

    /**
     *
     * @param d 查询数据方法参数
     * @param vClass 返回类型
     * @param getDataFun 查询数据函数式接口
     * @param outputStream 输出流
     * @param includeColumns 需要导出的列
     * @param writeHandlerList 自定义拦截器
     * @param converterList 自定义数据格式化转换器
     * @param <D>
     * @param <U>
     * @param <V>
     */
    public static <D, U, V> void export(D d, Class<V> vClass,
                                        @NotNull Function<D, List<V>> getDataFun,
                                        @NotNull OutputStream outputStream,
                                        @NotNull List<String> includeColumns,
                                        @Nullable List<? extends WriteHandler> writeHandlerList,
                                        @Nullable List<? extends Converter> converterList) {


        /**
         * 1、根据需要导出的列获取每列的列名和单元格的列宽
         */
        // 单元格宽度
        int columnIndex = 0;
        Map<Integer, Integer> columnWidthMap = new HashMap<>();
        //  获取表格头
        List<List<String>> headList = new ArrayList<>();
        List<Field> columnList = new ArrayList<>();
        Field[] declaredFields = vClass.getDeclaredFields();
        for (String includeColumn : includeColumns) {
            Optional<Field> includeColumnOptional = Arrays.stream(declaredFields).filter(f -> f.getName().equals(includeColumn))
                    .findFirst();
            if (includeColumnOptional.isPresent()) {
                Field field = includeColumnOptional.get();
                field.setAccessible(true);
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                if (!ObjectUtils.isEmpty(excelProperty)) {
                    // 列名
                    String[] columNameArray = excelProperty.value();
                    headList.add(Arrays.asList(columNameArray));

                    // 可导出的列
                    columnList.add(field);

                    // 保存每列的宽度
                    ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class);
                    columnWidthMap.put(columnWidth == null ? -1 : columnWidth.value(), columnIndex++);
                }
            }
        }

        /**
         * 2、根据需要导出的列获取需要显示的数据
         */
        List<List<Object>> exportDataList = new ArrayList<>();
        // 执行函数式接口获取需要导出的数据
        List<V> dataCollection = getDataFun.apply(d);

        for (V v : dataCollection) {

            // 拼装每行的数据
            List<Object> dataSubList = new ArrayList<>();
            for (Field field : columnList) {
                try {
                    Object columnValue = field.get(v);
                    dataSubList.add(Optional.ofNullable(columnValue).orElse(""));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            exportDataList.add(dataSubList);
        }

        // 表格处理策略
        ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream).head(headList);

        if (!ObjectUtils.isEmpty(writeHandlerList)) {
            writeHandlerList.forEach(writerBuilder::registerWriteHandler);
        }

        // 类型转换策略
        if (ObjectUtils.isEmpty(converterList)) {
            writerBuilder.registerConverter(new DataConverter.IntegerConverter());
            writerBuilder.registerConverter(new DataConverter.LocalDateConverter());
            writerBuilder.registerConverter(new DataConverter.LocalTimeConverter());
            writerBuilder.registerConverter(new DataConverter.LocalDateTimeConverter());
        } else {
            converterList.forEach(writerBuilder::registerConverter);
        }

        // 创建Sheet页
        String sheetName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
        // 设置列宽
        writeSheet.setColumnWidthMap(columnWidthMap);

        // 把数据写入到Sheet页中
        ExcelWriter excelWriter = writerBuilder.build();
        excelWriter.write(exportDataList, writeSheet);

        // 关闭流
        excelWriter.finish();

        // 关闭输出流
        IOUtils.closeQuietly(outputStream);

    }
}
